#include "Renderer/renderer2D.h"

#include "Renderer/vertexArray.h"
#include "Renderer/shader.h"
#include "Renderer/uniformBuffer.h"
#include "Renderer/renderCommand.h"

#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "Core/utils.h"
#include "renderer2D.h"

namespace CC
{

	struct QuadVertex
	{
		glm::vec3 Position;
		glm::vec4 Color;
		glm::vec2 TexCoord;
		float TexIndex;
		float TilingFactor;

		// Editor-only
		int EntityID;
	};

	struct Renderer2DData
	{
		static const uint32_t MaxQuads = 100;
		static const uint32_t MaxVertices = MaxQuads * 4;
		static const uint32_t MaxIndices = MaxQuads * 6;
		static const uint32_t MaxTextureSlots = 5;

		VertexArray *QuadVertexArray;
		VertexArray *FontVertexArray;
		VertexBuffer *QuadVertexBuffer;
		VertexBuffer *FontVertexBuffer;
		Shader *QuadShader;
		Shader *FontShader;

		uint32_t QuadIndexCount = 0;
		uint32_t FontIndexCount = 0;

		QuadVertex *QuadVertexBufferBase = nullptr;
		QuadVertex *QuadVertexBufferPtr = nullptr;

		QuadVertex *FontVertexBufferBase = nullptr;
		QuadVertex *FontVertexBufferPtr = nullptr;

		glm::vec4 QuadVertexPositions[4];
		// glm::vec4 FontVertexPositions[4];

		float LineWidth = 2.0f;

		Texture2D *TextureSlots[MaxTextureSlots];

		Renderer2D::Statistics Stats;

        Framebuffer *FramebufferDataCustom = nullptr;

		DisplayModesEnum Mode = DisplayModesEnum::DisplayModesEnum_TRIANGLES;

		struct CameraData
		{
			glm::mat4 ViewProjection;
		};
		CameraData CameraBuffer;
		UniformBuffer *CameraUniformBuffer;

		FontComponent fontComponent;			 // for font
		TransformComponent transformComponent;	 // for font
		glm::mat4 parentTransform;	 // for font
		Material2DComponent material2DComponent; // for font
	};

	static Renderer2DData s_Data;
	static uint32_t m_ViewportWidth;
	static uint32_t m_ViewportHeight;
	static uint32_t m_ViewportX;
	static uint32_t m_ViewportY;
	static Renderer2D::StatisEditText s_StatisEditText;

	static float CalcLineMaxHeight(std::string text, unsigned int fontSize, float scale, int lineHeightMargin = 2);
	static float CalcLineMaxHeightW(std::wstring text, unsigned int fontSize, float scale, int lineHeightMargin = 2);
	void CalcCursorPos(int rowIndex, int columIndex, int columSize, float charXpos, float charYpox,
					   float charWidth, float charHeight, float startX, float lineHeight, int lineHeightMargin = 2);

	bool CalcSelectArea(int rowIndex, int columIndex);

	void Renderer2D::Init()
	{
		s_Data.QuadVertexArray = VertexArray::Create();

		s_Data.QuadVertexBuffer = VertexBuffer::Create(s_Data.MaxVertices * sizeof(QuadVertex));
		s_Data.QuadVertexBuffer->SetLayout({{ShaderDataType::Float3, "a_Position"},
											{ShaderDataType::Float4, "a_Color"},
											{ShaderDataType::Float2, "a_TexCoord"},
											{ShaderDataType::Float, "a_TexIndex"},
											{ShaderDataType::Float, "a_TilingFactor"},
											{ShaderDataType::Int, "a_EntityID"}});
		s_Data.QuadVertexArray->AddVertexBuffer(s_Data.QuadVertexBuffer);

		s_Data.QuadVertexBufferBase = new QuadVertex[s_Data.MaxVertices];

		uint32_t *quadIndices = new uint32_t[s_Data.MaxIndices];

		uint32_t offset = 0;
		for (uint32_t i = 0; i < s_Data.MaxIndices; i += 6)
		{
			quadIndices[i + 0] = offset + 0;
			quadIndices[i + 1] = offset + 1;
			quadIndices[i + 2] = offset + 2;

			quadIndices[i + 3] = offset + 2;
			quadIndices[i + 4] = offset + 3;
			quadIndices[i + 5] = offset + 0;

			offset += 4;
		}

		IndexBuffer *quadIB = IndexBuffer::Create(quadIndices, s_Data.MaxIndices);
		s_Data.QuadVertexArray->SetIndexBuffer(quadIB);

		// font
		s_Data.FontVertexArray = VertexArray::Create();

		s_Data.FontVertexBuffer = VertexBuffer::Create(s_Data.MaxVertices * sizeof(QuadVertex));
		s_Data.FontVertexBuffer->SetLayout({{ShaderDataType::Float3, "a_Position"},
											{ShaderDataType::Float4, "a_Color"},
											{ShaderDataType::Float2, "a_TexCoord"},
											{ShaderDataType::Float, "a_TexIndex"},
											{ShaderDataType::Float, "a_TilingFactor"},
											{ShaderDataType::Int, "a_EntityID"}});
		s_Data.FontVertexArray->AddVertexBuffer(s_Data.FontVertexBuffer);

		s_Data.FontVertexBufferBase = new QuadVertex[s_Data.MaxVertices];

		IndexBuffer *fontIB = IndexBuffer::Create(quadIndices, s_Data.MaxIndices);
		s_Data.FontVertexArray->SetIndexBuffer(fontIB);
		delete[] quadIndices;

		s_Data.QuadShader = Shader::Create("Renderer2D_Quad", ("Renderer2D_Quad"), ("Renderer2D_Quad"));

		s_Data.QuadVertexPositions[0] = {0.0f, 0.0f, 0.0f, 1.0f}; // left down
		s_Data.QuadVertexPositions[1] = {1.0f, 0.0f, 0.0f, 1.0f}; // right down
		s_Data.QuadVertexPositions[2] = {1.0f, 1.0f, 0.0f, 1.0f}; // right up
		s_Data.QuadVertexPositions[3] = {0.0f, 1.0f, 0.0f, 1.0f}; // left up

		for (uint32_t i = 0; i < s_Data.MaxTextureSlots; i++)
		{
			s_Data.TextureSlots[i] = nullptr;
		}

		// font
		// s_Data.FontShader = Shader::Create("RendererFont", PATHCONFIG("assets/shaders/Font.vert"), PATHCONFIG("assets/shaders/Font.frag"));
		s_Data.FontShader = Shader::Create("RendererFont", ("Font"), ("Font"));

		// s_Data.CameraUniformBuffer = UniformBuffer::Create(sizeof(Renderer2DData::CameraData), "Camera", s_Data.QuadShader->GetProgramID());
	}

	void Renderer2D::Shutdown()
	{
		delete[] s_Data.QuadVertexBufferBase;
		delete[] s_Data.FontVertexBufferBase;
		delete s_Data.QuadVertexArray;
		delete s_Data.FontVertexArray;
	}

	void Renderer2D::BeginScene(const Camera *camera, const glm::mat4 &transform, Framebuffer* framebuffer)
	{
		// CC_CORE_INFO("Renderer2D::BeginScene()");

        if (framebuffer) {
            s_Data.FramebufferDataCustom = framebuffer;
            s_Data.FramebufferDataCustom->Bind();

            RenderCommand::ClearDepth();
            RenderCommand::Clear();
        }
        else {
            s_Data.FramebufferDataCustom = nullptr;
            RenderCommand::SetViewport(m_ViewportX, m_ViewportY, m_ViewportWidth, m_ViewportHeight);
        }

		// RenderCommand::Clear();

		s_Data.CameraBuffer.ViewProjection = camera->GetProjection() * glm::inverse(transform);
		// s_Data.CameraUniformBuffer->SetData(&s_Data.CameraBuffer, sizeof(Renderer2DData::CameraData));

		s_Data.QuadShader->Bind();
		s_Data.QuadShader->SetMat4("u_ViewProjection", s_Data.CameraBuffer.ViewProjection);

		s_Data.FontShader->Bind();
		s_Data.FontShader->SetMat4("u_ViewProjection", s_Data.CameraBuffer.ViewProjection);

		StartBatch();
	}

	void Renderer2D::EndScene()
	{
		Flush();
		// CC_CORE_INFO("Renderer2D::EndScene()");
        if (s_Data.FramebufferDataCustom) {
            s_Data.FramebufferDataCustom->Unbind();
        }
	}

	void Renderer2D::StartBatch()
	{
		s_Data.QuadIndexCount = 0;
		s_Data.QuadVertexBufferPtr = s_Data.QuadVertexBufferBase;

		s_Data.FontIndexCount = 0;
		s_Data.FontVertexBufferPtr = s_Data.FontVertexBufferBase;
	}

	void Renderer2D::Flush()
	{
		if (s_Data.QuadIndexCount)
		{
			uint32_t dataSize = (uint32_t)((uint8_t *)s_Data.QuadVertexBufferPtr - (uint8_t *)s_Data.QuadVertexBufferBase);
			s_Data.QuadVertexBuffer->SetData(s_Data.QuadVertexBufferBase, dataSize);

			s_Data.QuadShader->Bind();

			for (uint32_t i = 0; i < s_Data.MaxTextureSlots; i++)
			{
				if (s_Data.TextureSlots[i])
				{
					s_Data.TextureSlots[i]->Bind(i);
					s_Data.QuadShader->SetInt("u_Textures[" + Tool::Int2Str(i) + "]", i);
				}
			}

			RenderCommand::DrawIndexed(s_Data.QuadVertexArray, s_Data.QuadIndexCount, s_Data.Mode);

			RenderCommand::UnBindTexture();
			s_Data.Stats.DrawCalls++;
		}

		if (s_Data.FontIndexCount)
		{
			uint32_t dataSize = (uint32_t)((uint8_t *)s_Data.FontVertexBufferPtr - (uint8_t *)s_Data.FontVertexBufferBase);
			s_Data.FontVertexBuffer->SetData(s_Data.FontVertexBufferBase, dataSize);

			s_Data.FontShader->Bind();
			s_Data.FontShader->SetInt("u_Textures[0]", 0);

			// 遍历文本中所有的字符
			FontComponent fontComponent = s_Data.fontComponent;
			TransformComponent transformComponent = s_Data.transformComponent;
			glm::mat4 parentTransform = s_Data.parentTransform;
			Material2DComponent sprite = s_Data.material2DComponent;
			// glm::vec4 selectColor = {1.0 - sprite.Color[0], 1.0 - sprite.Color[1], 1.0 - sprite.Color[2], 1.0};
			glm::vec4 selectColor = {0.0, 0.0, 0.5, 1.0};

			glm::vec4 Translation = parentTransform * glm::vec4(transformComponent.Translation[0], transformComponent.Translation[1], transformComponent.Translation[2], 1.0);

			float scale = fontComponent.scale;
			//std::string textAll = fontComponent.text;
			std::wstring wtextAll = fontComponent.wtext;
			Font font = *fontComponent.font;
			unsigned int fontSize = fontComponent.fontSize;
            //return;
			// optimize
			//unsigned int _cacheFontSize = fontSize > FONTCACHEVALUE ? FONTCACHEVALUE * 2 : FONTCACHEVALUE;
            unsigned int _cacheFontSize = FONTCACHEVALUE;

			// cache
			scale = (float)(fontSize) / (float)(_cacheFontSize);

			std::vector<std::wstring> listw;
            const static std::wstring _n = string_to_wstring("\n");
			FileLog::SplitWStr(wtextAll, _n, listw);

			int lenList = listw.size();
			float aboveHeight = 0.0;
			const int lineHeightSpace = 8;
			//float lastLineHeight = _cacheFontSize == FONTCACHEVALUE ? (22 + lineHeightSpace) : (46 + lineHeightSpace);
			float lastLineHeight = 22 + lineHeightSpace;
            //return;
            for (int row = 0; row < lenList; ++row) {
                // get max height
                float maxHeightLine = 0.0;
                maxHeightLine = CalcLineMaxHeightW(wtextAll, _cacheFontSize, scale);
                lastLineHeight = maxHeightLine != 0.0 ? maxHeightLine : lastLineHeight;

                std::wstring line = listw[row];
                int len = line.size();

                float x = 0.0;
                float y = aboveHeight;
                aboveHeight += lastLineHeight;

                for (int column = 0; column < len; ++column)
                {
                    
                    wchar_t char_code = line[column];
                    Character* character = LoadCharacterW(font, char_code, FONTCACHEVALUE);
                    if (!character || !character->texture2D) {
                        continue;
                    }
                    GLfloat xpos = x + Translation[0] + character->bearing.x * scale;
                    GLfloat ypos = y + Translation[1];
                    // xpos,ypos为位图左下角原点

                    GLfloat w = character->size.x * scale;
                    GLfloat h = character->size.y * scale;

                    // update cursor
                    //CalcCursorPos(row, column, lenList, xpos, ypos, w, h, Translation[0], lastLineHeight);

                    bool needDrawSelectArea = false;

                    //needDrawSelectArea = CalcSelectArea(row, column);

                    if (needDrawSelectArea)
                    {
                        float xxpos = x + Translation[0];
                        float ww = (character->advance >> 6) * scale;
                        float hh = lastLineHeight;
                        glm::mat4 model = glm::mat4(1.0f);
                        model = glm::translate(model, glm::vec3(xxpos, ypos, 0.0)); // first translate (transformations are: scale happens first, then rotation, and then final translation happens; reversed order)

                        model = glm::translate(model, glm::vec3(0.5f * ww, 0.5f * hh, 0.0f));		 // move origin of rotation to center of quad
                        model = glm::rotate(model, glm::radians(0.0f), glm::vec3(1.0f, 0.0f, 0.0f)); // then rotate
                        model = glm::translate(model, glm::vec3(-0.5f * ww, -0.5f * hh, 0.0f));		 // move origin back

                        model = glm::scale(model, glm::vec3(ww, hh, 1.0f)); // last scale

                        s_Data.FontShader->SetMat4("u_Model", model);

                        s_Data.FontShader->SetFloat("Select", 1.0);
                        s_Data.FontShader->SetFloat4("SelectColor", selectColor);
                        RenderCommand::DrawIndexed(s_Data.FontVertexArray, s_Data.FontIndexCount, s_Data.Mode);
                        s_Data.Stats.DrawCalls++;
                        s_Data.FontShader->SetFloat("Select", 0.0);
                    }

                    glm::mat4 model = glm::mat4(1.0f);
                    // p g 字母需要下移
                    float charOffsetY = 0.0;
                    if (char_code == L'p' || char_code == L'g' || char_code == L'j'
                        || char_code == L'q' || char_code == L'y') {
                        charOffsetY = 12.0 * scale;
                    }
                    model = glm::translate(model, glm::vec3(xpos, ypos + (lastLineHeight - h) + charOffsetY, 0.0)); // first translate (transformations are: scale happens first, then rotation, and then final translation happens; reversed order)

                    model = glm::translate(model, glm::vec3(0.5f * w, 0.5f * h, 0.0f));			 // move origin of rotation to center of quad
                    model = glm::rotate(model, glm::radians(0.0f), glm::vec3(1.0f, 0.0f, 0.0f)); // then rotate
                    model = glm::translate(model, glm::vec3(-0.5f * w, -0.5f * h, 0.0f));		 // move origin back

                    model = glm::scale(model, glm::vec3(w, h, 1.0f)); // last scale

                    s_Data.FontShader->SetMat4("u_Model", model);
                    character->texture2D->Bind(0);
                    RenderCommand::DrawIndexed(s_Data.FontVertexArray, s_Data.FontIndexCount, s_Data.Mode);
                    s_Data.Stats.DrawCalls++;
                    // 更新位置到下一个字形的原点，注意单位是1/64像素
                    x += (character->advance >> 6) * scale; // 位偏移6个单位来获取单位为像素的值 (2^6 = 64)

                    // for space line
                    //if (0 == len)
                    //{
                    //    GLfloat xpos = Translation[0];
                    //    GLfloat ypos = y + Translation[1];
                    //
                    //    CalcCursorPos(row, 0, 0, xpos, ypos, 0.0, 0.0, Translation[0], lastLineHeight);
                    //}
                }

            }
            
			
			RenderCommand::UnBindTexture();
		}
	}

	void Renderer2D::NextBatch()
	{
		Flush();
		StartBatch();
	}

	void Renderer2D::DrawQuad(const glm::vec2 &position, const glm::vec2 &size, const glm::vec4 &color)
	{
		DrawQuad({position.x, position.y, 0.0f}, size, color);
	}

	void Renderer2D::DrawQuad(const glm::vec3 &position, const glm::vec2 &size, const glm::vec4 &color)
	{
		glm::mat4 transform = glm::translate(glm::mat4(1.0f), position) * glm::scale(glm::mat4(1.0f), {size.x, size.y, 1.0f});

		DrawQuad(transform, color);
	}

	void Renderer2D::DrawQuad(const glm::vec3 &position, const glm::vec2 &size, Texture2D *texture, float tilingFactor, const glm::vec4 &tintColor)
	{
		glm::mat4 transform = glm::translate(glm::mat4(1.0f), position) * glm::scale(glm::mat4(1.0f), {size.x, size.y, 1.0f});

		DrawQuad(transform, texture, tilingFactor, tintColor);
	}

	void Renderer2D::DrawQuad(const glm::mat4 &transform, Texture2D *texture, float tilingFactor, const glm::vec4 &tintColor, int entityID)
	{
		NextBatch();

		constexpr size_t quadVertexCount = 4;
		const float textureIndex = 0.0f; // White Texture
		constexpr glm::vec2 textureCoords[] = {{0.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, 1.0f}, {0.0f, 1.0f}};

		s_Data.TextureSlots[0] = texture;

		for (size_t i = 0; i < quadVertexCount; i++)
		{
			s_Data.QuadVertexBufferPtr->Position = transform * s_Data.QuadVertexPositions[i];
			s_Data.QuadVertexBufferPtr->Color = tintColor;
			s_Data.QuadVertexBufferPtr->TexCoord = textureCoords[i];
			s_Data.QuadVertexBufferPtr->TexIndex = textureIndex;
			s_Data.QuadVertexBufferPtr->TilingFactor = tilingFactor;
			s_Data.QuadVertexBufferPtr->EntityID = entityID;
			s_Data.QuadVertexBufferPtr++;
		}

		s_Data.QuadIndexCount += 6;

		s_Data.Stats.QuadCount++;
	}

    void Renderer2D::DrawQuad(const glm::mat4 & transform, Texture2D * texture, ShaderComponent & shaderc, Custom2DMaterialComponent* custom2DMaterial, float tilingFactor, const glm::vec4 & tintColor, int entityID)
    {
        NextBatch();

        shaderc.shader->Bind();
        shaderc.shader->SetMat4("u_ViewProjection", s_Data.CameraBuffer.ViewProjection);

        constexpr size_t quadVertexCount = 4;
        const float textureIndex = 0.0f; // White Texture
        constexpr glm::vec2 textureCoords[] = { {0.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, 1.0f}, {0.0f, 1.0f} };

        for (size_t i = 0; i < quadVertexCount; i++)
        {
            s_Data.QuadVertexBufferPtr->Position = transform * s_Data.QuadVertexPositions[i];
            s_Data.QuadVertexBufferPtr->Color = tintColor;
            s_Data.QuadVertexBufferPtr->TexCoord = textureCoords[i];
            s_Data.QuadVertexBufferPtr->TexIndex = textureIndex;
            s_Data.QuadVertexBufferPtr->TilingFactor = tilingFactor;
            s_Data.QuadVertexBufferPtr->EntityID = entityID;
            s_Data.QuadVertexBufferPtr++;
        }

        s_Data.QuadIndexCount = 6;
        s_Data.Stats.QuadCount++;

        if (custom2DMaterial) {
            size_t texCountCustom = 5;
            std::unordered_map<std::string, Custom2DMaterialComponent::DataInfo>::iterator it = custom2DMaterial->datas.begin();
            for (it; it != custom2DMaterial->datas.end(); ++it)
            {
                if (Custom2DMaterialComponent::DataType_Int == it->second.dataType) {
                    shaderc.shader->SetInt(it->first, it->second.a4[0]);
                }
                else if (Custom2DMaterialComponent::DataType_Int2 == it->second.dataType) {
                    shaderc.shader->SetIntArray(it->first, &it->second.a4[0], 2);
                }
                else if (Custom2DMaterialComponent::DataType_Int3 == it->second.dataType) {
                    shaderc.shader->SetIntArray(it->first, &it->second.a4[0], 3);
                }
                else if (Custom2DMaterialComponent::DataType_Int4 == it->second.dataType) {
                    shaderc.shader->SetIntArray(it->first, &it->second.a4[0], 4);
                }
                else if (Custom2DMaterialComponent::DataType_Float == it->second.dataType) {
                    shaderc.shader->SetFloat(it->first, it->second.b4[0]);
                }
                else if (Custom2DMaterialComponent::DataType_Float2 == it->second.dataType) {
                    shaderc.shader->SetFloat2(it->first, glm::vec2(it->second.b4[0], it->second.b4[1]));
                }
                else if (Custom2DMaterialComponent::DataType_Float3 == it->second.dataType) {
                    shaderc.shader->SetFloat3(it->first, glm::vec3(it->second.b4[0], it->second.b4[1], it->second.b4[2]));
                }
                else if (Custom2DMaterialComponent::DataType_Float4 == it->second.dataType) {
                    shaderc.shader->SetFloat4(it->first, glm::vec4(it->second.b4[0], it->second.b4[1], it->second.b4[2], it->second.b4[3]));
                }
                else if (Custom2DMaterialComponent::DataType_Texture == it->second.dataType) {
                    Texture2D* tex = it->second.texture2D;
                    shaderc.shader->SetInt(it->first, texCountCustom);
                    tex->Bind(texCountCustom);
                    texCountCustom += 1;
                }
            }

        }

        {
            uint32_t dataSize = (uint32_t)((uint8_t *)s_Data.QuadVertexBufferPtr - (uint8_t *)s_Data.QuadVertexBufferBase);
            s_Data.QuadVertexBuffer->SetData(s_Data.QuadVertexBufferBase, dataSize);

            texture->Bind(0);
            shaderc.shader->SetInt("u_Textures[0]", 0);

            RenderCommand::DrawIndexed(s_Data.QuadVertexArray, s_Data.QuadIndexCount, s_Data.Mode);

            RenderCommand::UnBindTexture();
            s_Data.Stats.DrawCalls++;
        }

        s_Data.QuadIndexCount = 0;
        s_Data.QuadVertexBufferPtr = s_Data.QuadVertexBufferBase;
        
    }

	void Renderer2D::DrawQuad(const glm::mat4 &transform, const glm::vec4 &color, int entityID)
	{
		NextBatch();

		constexpr size_t quadVertexCount = 4;
		const float textureIndex = 100.0f; // White Texture
		constexpr glm::vec2 textureCoords[] = {{0.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, 1.0f}, {0.0f, 1.0f}};
		const float tilingFactor = 1.0f;

		for (uint32_t i = 0; i < s_Data.MaxTextureSlots; i++)
		{
			s_Data.TextureSlots[i] = nullptr;
		}

		for (size_t i = 0; i < quadVertexCount; i++)
		{
			s_Data.QuadVertexBufferPtr->Position = transform * s_Data.QuadVertexPositions[i];
			s_Data.QuadVertexBufferPtr->Color = color;
			s_Data.QuadVertexBufferPtr->TexCoord = textureCoords[i];
			s_Data.QuadVertexBufferPtr->TexIndex = textureIndex;
			s_Data.QuadVertexBufferPtr->TilingFactor = tilingFactor;
			s_Data.QuadVertexBufferPtr->EntityID = entityID;
			s_Data.QuadVertexBufferPtr++;
		}

		s_Data.QuadIndexCount += 6;

		s_Data.Stats.QuadCount++;
	}

    void Renderer2D::DrawQuad(const glm::mat4 & transform, const glm::vec4 & color, ShaderComponent & shaderc, int entityID)
    {
        NextBatch();

        shaderc.shader->Bind();
        shaderc.shader->SetMat4("u_ViewProjection", s_Data.CameraBuffer.ViewProjection);

        constexpr size_t quadVertexCount = 4;
        const float textureIndex = 100.0f; // White Texture
        constexpr glm::vec2 textureCoords[] = { {0.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, 1.0f}, {0.0f, 1.0f} };
        const float tilingFactor = 1.0f;

        for (uint32_t i = 0; i < s_Data.MaxTextureSlots; i++)
        {
            s_Data.TextureSlots[i] = nullptr;
        }

        for (size_t i = 0; i < quadVertexCount; i++)
        {
            s_Data.QuadVertexBufferPtr->Position = transform * s_Data.QuadVertexPositions[i];
            s_Data.QuadVertexBufferPtr->Color = color;
            s_Data.QuadVertexBufferPtr->TexCoord = textureCoords[i];
            s_Data.QuadVertexBufferPtr->TexIndex = textureIndex;
            s_Data.QuadVertexBufferPtr->TilingFactor = tilingFactor;
            s_Data.QuadVertexBufferPtr->EntityID = entityID;
            s_Data.QuadVertexBufferPtr++;
        }

        s_Data.QuadIndexCount = 6;

        s_Data.Stats.QuadCount++;

        {
            uint32_t dataSize = (uint32_t)((uint8_t *)s_Data.QuadVertexBufferPtr - (uint8_t *)s_Data.QuadVertexBufferBase);
            s_Data.QuadVertexBuffer->SetData(s_Data.QuadVertexBufferBase, dataSize);

            RenderCommand::DrawIndexed(s_Data.QuadVertexArray, s_Data.QuadIndexCount, s_Data.Mode);

            RenderCommand::UnBindTexture();
            s_Data.Stats.DrawCalls++;
        }

        s_Data.QuadIndexCount = 0;
        s_Data.QuadVertexBufferPtr = s_Data.QuadVertexBufferBase;
    }

	void Renderer2D::DrawSprite(const glm::mat4 &transform, Material2DComponent &src, int entityID)
	{
		if (src.Texture)
			DrawQuad(transform, src.Texture, src.TilingFactor, src.Color, entityID);
		else
			DrawQuad(transform, src.Color, entityID);
	}

    void Renderer2D::DrawSprite(const glm::mat4 & transform, Material2DComponent & src, ShaderComponent & shaderc, Custom2DMaterialComponent* custom2DMaterial, int entityID)
    {
        if (src.Texture)
            DrawQuad(transform, src.Texture, shaderc, custom2DMaterial, src.TilingFactor, src.Color, entityID);
        else
            DrawQuad(transform, src.Color, shaderc, entityID);
    }

    void Renderer2D::DrawPriticle(const glm::mat4 &transform, Material2DComponent &src, EmitterComponent &emitter, Particle2DComponent &particle, int entityID)
    {
		if(!src.Texture) return;
		// if (src.Texture)
        //     DrawQuadParticle(transform, src.Texture, src.TilingFactor, src.Color, entityID);
        // else
        //     DrawQuadParticle(transform, src.Color, entityID);
		NextBatch();

		constexpr size_t quadVertexCount = 4;
		const float textureIndex = 0.0f; // White Texture
		constexpr glm::vec2 textureCoords[] = {{0.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, 1.0f}, {0.0f, 1.0f}};

		s_Data.TextureSlots[0] = src.Texture;

		for(size_t index = 0; index < emitter.poolsize; ++index){
			EmitterComponent::Particle& particle = emitter.GetParticle(index);
			if(particle.Life > 0.0f){
				for (size_t i = 0; i < quadVertexCount; i++)
				{
					s_Data.QuadVertexBufferPtr->Position = particle.Tran.GetTransform() * s_Data.QuadVertexPositions[i];
					s_Data.QuadVertexBufferPtr->Color = particle.Color;
					s_Data.QuadVertexBufferPtr->TexCoord = textureCoords[i];
					s_Data.QuadVertexBufferPtr->TexIndex = textureIndex;
					s_Data.QuadVertexBufferPtr->TilingFactor = src.TilingFactor;
					s_Data.QuadVertexBufferPtr->EntityID = entityID;
					s_Data.QuadVertexBufferPtr++;
				}
				s_Data.QuadIndexCount += 6;
				s_Data.Stats.QuadCount++;
			}
		}
    }

    void Renderer2D::DrawPriticle(const glm::mat4 &transform, Material2DComponent &src, EmitterComponent &emitter, Particle2DComponent &particle, ShaderComponent &shaderc, Custom2DMaterialComponent *custom2DMaterial, int entityID)
    {
		if(!src.Texture) return;
		// if (src.Texture)
        //     DrawQuadParticle(transform, src.Texture, shaderc, custom2DMaterial, src.TilingFactor, src.Color, entityID);
        // else
        //     DrawQuadParticle(transform, src.Color, shaderc, entityID);
		NextBatch();

        shaderc.shader->Bind();
        shaderc.shader->SetMat4("u_ViewProjection", s_Data.CameraBuffer.ViewProjection);

        constexpr size_t quadVertexCount = 4;
        const float textureIndex = 0.0f; // White Texture
        constexpr glm::vec2 textureCoords[] = { {0.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, 1.0f}, {0.0f, 1.0f} };

        for(size_t index = 0; index < emitter.poolsize; ++index){
			EmitterComponent::Particle& particle = emitter.GetParticle(index);
			if(particle.Life > 0.0f){
				for (size_t i = 0; i < quadVertexCount; i++)
				{
					s_Data.QuadVertexBufferPtr->Position = particle.Tran.GetTransform() * s_Data.QuadVertexPositions[i];
					s_Data.QuadVertexBufferPtr->Color = particle.Color;
					s_Data.QuadVertexBufferPtr->TexCoord = textureCoords[i];
					s_Data.QuadVertexBufferPtr->TexIndex = textureIndex;
					s_Data.QuadVertexBufferPtr->TilingFactor = src.TilingFactor;
					s_Data.QuadVertexBufferPtr->EntityID = entityID;
					s_Data.QuadVertexBufferPtr++;
				}
				s_Data.QuadIndexCount += 6;
				s_Data.Stats.QuadCount++;
			}
		}

        if (custom2DMaterial) {
            size_t texCountCustom = 5;
            std::unordered_map<std::string, Custom2DMaterialComponent::DataInfo>::iterator it = custom2DMaterial->datas.begin();
            for (it; it != custom2DMaterial->datas.end(); ++it)
            {
                if (Custom2DMaterialComponent::DataType_Int == it->second.dataType) {
                    shaderc.shader->SetInt(it->first, it->second.a4[0]);
                }
                else if (Custom2DMaterialComponent::DataType_Int2 == it->second.dataType) {
                    shaderc.shader->SetIntArray(it->first, &it->second.a4[0], 2);
                }
                else if (Custom2DMaterialComponent::DataType_Int3 == it->second.dataType) {
                    shaderc.shader->SetIntArray(it->first, &it->second.a4[0], 3);
                }
                else if (Custom2DMaterialComponent::DataType_Int4 == it->second.dataType) {
                    shaderc.shader->SetIntArray(it->first, &it->second.a4[0], 4);
                }
                else if (Custom2DMaterialComponent::DataType_Float == it->second.dataType) {
                    shaderc.shader->SetFloat(it->first, it->second.b4[0]);
                }
                else if (Custom2DMaterialComponent::DataType_Float2 == it->second.dataType) {
                    shaderc.shader->SetFloat2(it->first, glm::vec2(it->second.b4[0], it->second.b4[1]));
                }
                else if (Custom2DMaterialComponent::DataType_Float3 == it->second.dataType) {
                    shaderc.shader->SetFloat3(it->first, glm::vec3(it->second.b4[0], it->second.b4[1], it->second.b4[2]));
                }
                else if (Custom2DMaterialComponent::DataType_Float4 == it->second.dataType) {
                    shaderc.shader->SetFloat4(it->first, glm::vec4(it->second.b4[0], it->second.b4[1], it->second.b4[2], it->second.b4[3]));
                }
                else if (Custom2DMaterialComponent::DataType_Texture == it->second.dataType) {
                    Texture2D* tex = it->second.texture2D;
                    shaderc.shader->SetInt(it->first, texCountCustom);
                    tex->Bind(texCountCustom);
                    texCountCustom += 1;
                }
            }

        }

        {
            uint32_t dataSize = (uint32_t)((uint8_t *)s_Data.QuadVertexBufferPtr - (uint8_t *)s_Data.QuadVertexBufferBase);
            s_Data.QuadVertexBuffer->SetData(s_Data.QuadVertexBufferBase, dataSize);

            src.Texture->Bind(0);
            shaderc.shader->SetInt("u_Textures[0]", 0);

            RenderCommand::DrawIndexed(s_Data.QuadVertexArray, s_Data.QuadIndexCount, s_Data.Mode);

            RenderCommand::UnBindTexture();
            s_Data.Stats.DrawCalls++;
        }

        s_Data.QuadIndexCount = 0;
        s_Data.QuadVertexBufferPtr = s_Data.QuadVertexBufferBase;
    }

    void Renderer2D::DrawFont(const glm::mat4 &parentTransform, FontComponent &fontComponent, TransformComponent &transformComponent, Material2DComponent &src, int entityID)
    {

		NextBatch();

		constexpr size_t quadVertexCount = 4;
		const float textureIndex = 0.0f; // White Texture
		constexpr glm::vec2 textureCoords[] = {{0.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, 1.0f}, {0.0f, 1.0f}};

		for (uint32_t i = 0; i < s_Data.MaxTextureSlots; i++)
		{
			s_Data.TextureSlots[i] = nullptr;
		}

		for (size_t i = 0; i < quadVertexCount; i++)
		{
			s_Data.FontVertexBufferPtr->Position = s_Data.QuadVertexPositions[i];
			s_Data.FontVertexBufferPtr->Color = src.Color;
			s_Data.FontVertexBufferPtr->TexCoord = textureCoords[i];
			s_Data.FontVertexBufferPtr->TexIndex = textureIndex;
			s_Data.FontVertexBufferPtr->TilingFactor = src.TilingFactor;
			s_Data.FontVertexBufferPtr->EntityID = entityID;
			s_Data.FontVertexBufferPtr++;
		}

		s_Data.fontComponent = fontComponent;
		s_Data.parentTransform = parentTransform;
		s_Data.transformComponent = transformComponent;
		s_Data.material2DComponent = src;

		s_Data.FontIndexCount += 6;

		s_Data.Stats.QuadCount++;
	}

	float Renderer2D::GetLineWidth()
	{
		return s_Data.LineWidth;
	}

	void Renderer2D::SetLineWidth(float width)
	{
		s_Data.LineWidth = width;
	}

	void Renderer2D::SetDisplayMode(DisplayModesEnum mode)
	{
		s_Data.Mode = mode;
	}

    Texture2D *Renderer2D::GetCustomDepthAttachmentTexture2D()
    {
        if (!s_Data.FramebufferDataCustom) return NULL;
        return s_Data.FramebufferDataCustom->GetDepthAttachmentTexture2D();
    }

    Texture2D *Renderer2D::GetCustomColor0AttachmentTexture2D()
    {
        if (!s_Data.FramebufferDataCustom) return NULL;
        return s_Data.FramebufferDataCustom->GetColor0AttachmentTexture2D();
    }

	void Renderer2D::ResetStats()
	{
		memset(&s_Data.Stats, 0, sizeof(Statistics));
	}

	Renderer2D::Statistics Renderer2D::GetStats()
	{
		return s_Data.Stats;
	}

	Renderer2D::StatisEditText &Renderer2D::GetStatisEditText()
	{
		return s_StatisEditText;
	}

	void Renderer2D::OnViewportResize(uint32_t x, uint32_t y, uint32_t width, uint32_t height)
	{
		m_ViewportX = x;
		m_ViewportY = y;
		m_ViewportWidth = width;
		m_ViewportHeight = height;
		RenderCommand::SetViewport(x, y, m_ViewportWidth, m_ViewportHeight);
	}

	//
	float CalcLineMaxHeight(std::string text, unsigned int fontSize, float scale, int lineHeightMargin)
	{
		int len = text.size();
		float maxHeightLine = 0.0;

		for (int i = 0; i < len; i++)
		{
			std::string one = text.substr(i, 1).c_str();

			signed long char_code = text[i];
			if (char_code < 0)
			{
				// 2 char
				if (i + 1 < len)
				{
					one = text.substr(i, 2).c_str();
					i++;
				}
			}
			else
			{
				//            printf("draw ascii %d: %s, %ld\n", i, one.c_str(), char_code);
			}

			Character character = GetCharacter(one, fontSize);
			if (!character.texture2D)
				continue;

			maxHeightLine = max(maxHeightLine, character.size.y * scale);
			if (i == len - 1)
			{
				maxHeightLine += lineHeightMargin * 2;
			}
			// CC_CORE_INFO("%d: maxHeight %s = %d/%f\n", i, text.substr(i, 1).c_str(), character.size.y, maxHeightLine);
		}
		return maxHeightLine;
	}

    float CalcLineMaxHeightW(std::wstring text, unsigned int fontSize, float scale, int lineHeightMargin)
    {
        int len = text.size();
        float maxHeightLine = 0.0;

        int i = 0;
        for (wchar_t char_code : text)
        {
            Character* character = GetCharacterW(char_code, fontSize);
            if (!character || !character->texture2D)
                continue;

            maxHeightLine = max(maxHeightLine, character->size.y * scale);
            if (i == len - 1)
            {
                maxHeightLine += lineHeightMargin * 2;
            }
            ++i;
            // CC_CORE_INFO("%d: maxHeight %s = %d/%f\n", i, text.substr(i, 1).c_str(), character.size.y, maxHeightLine);
        }
        return maxHeightLine;
    }

	void CalcCursorPos(int rowIndex, int columIndex, int columSize, float charXpos, float charYpox,
					   float charWidth, float charHeight, float startX, float lineHeight, int lineHeightMargin)
	{
		if (s_StatisEditText.UserClick)
		{
			glm::vec2 clickpos = s_StatisEditText.ClickPos;
			if (s_StatisEditText.UserSelect)
			{
				clickpos = s_StatisEditText.ClickPosEnd;
			}

			if (clickpos[1] > charYpox && clickpos[1] <= charYpox + lineHeight)
			{
				s_StatisEditText.CursorDisplayPos[1] = charYpox + lineHeightMargin;
				s_StatisEditText.CursorDisplayIndex[1] = rowIndex;

				if (charWidth == 0.0 && charHeight == 0.0)
				{
					// space line
					s_StatisEditText.CursorDisplayPos[0] = startX;
					s_StatisEditText.CursorDisplayIndex[0] = -1;
				}
				else if (columIndex == 0 && clickpos[0] <= charXpos + charWidth / 2.0)
				{
					s_StatisEditText.CursorDisplayPos[0] = startX;
					s_StatisEditText.CursorDisplayIndex[0] = -1;
				}
				else if (clickpos[0] > charXpos + charWidth / 2.0 && clickpos[0] < charXpos + charWidth * 1.5)
				{
					s_StatisEditText.CursorDisplayPos[0] = charXpos + charWidth;
					s_StatisEditText.CursorDisplayIndex[0] = columIndex;
				}
				else
				{
					if (clickpos[0] >= charXpos + charWidth * 1.5 && columIndex == columSize - 1)
					{
						s_StatisEditText.CursorDisplayPos[0] = charXpos + charWidth;
						s_StatisEditText.CursorDisplayIndex[0] = columIndex;
					}
				}
				s_StatisEditText.CursorHeight = lineHeight;
			}
			if (s_StatisEditText.UserSelect)
			{
			}
			else
			{
				s_StatisEditText.CursorDisplaySelectStartIndex = s_StatisEditText.CursorDisplayIndex;
			}
		}
		else
		{
			glm::ivec2 cursorMoveIndex = s_StatisEditText.CursorMoveIndex;
			if (s_StatisEditText.UserSelect)
			{
				cursorMoveIndex = s_StatisEditText.CursorMoveEndIndex;
			}

			if (cursorMoveIndex[1] == rowIndex)
			{
				s_StatisEditText.CursorDisplayPos[1] = charYpox + lineHeightMargin;
				s_StatisEditText.CursorDisplayIndex[1] = rowIndex;

				if (charWidth == 0.0 && charHeight == 0.0)
				{
					// space line
					s_StatisEditText.CursorDisplayPos[0] = startX;
					s_StatisEditText.CursorDisplayIndex[0] = -1;
				}
				else if (cursorMoveIndex[0] >= columIndex && cursorMoveIndex[0] < columIndex + 1)
				{
					s_StatisEditText.CursorDisplayPos[0] = charXpos + charWidth;
					s_StatisEditText.CursorDisplayIndex[0] = columIndex;
				}
				else if (cursorMoveIndex[0] <= -1)
				{
					s_StatisEditText.CursorDisplayPos[0] = startX;
					s_StatisEditText.CursorDisplayIndex[0] = -1;
				}
			}
			if (s_StatisEditText.UserSelect)
			{
			}
			else
			{
				s_StatisEditText.CursorDisplaySelectStartIndex = s_StatisEditText.CursorDisplayIndex;
			}
		}
	}

	bool CalcSelectArea(int rowIndex, int columIndex)
	{
		bool needDrawSelectArea = false;
		if (s_StatisEditText.UserSelect)
		{
			if (rowIndex == s_StatisEditText.CursorDisplaySelectStartIndex[1] && s_StatisEditText.CursorDisplaySelectStartIndex[1] == s_StatisEditText.CursorDisplayIndex[1])
			{
				// one line
				if ((columIndex > s_StatisEditText.CursorDisplaySelectStartIndex[0] && columIndex <= s_StatisEditText.CursorDisplayIndex[0]) || (columIndex > s_StatisEditText.CursorDisplayIndex[0] && columIndex <= s_StatisEditText.CursorDisplaySelectStartIndex[0]))
				{
					needDrawSelectArea = true;
				}
			}
			else if (s_StatisEditText.CursorDisplaySelectStartIndex[1] < s_StatisEditText.CursorDisplayIndex[1])
			{
				if (rowIndex == s_StatisEditText.CursorDisplaySelectStartIndex[1])
				{
					if (columIndex > s_StatisEditText.CursorDisplaySelectStartIndex[0])
						needDrawSelectArea = true;
				}
				else if (rowIndex == s_StatisEditText.CursorDisplayIndex[1])
				{
					if (columIndex <= s_StatisEditText.CursorDisplayIndex[0])
						needDrawSelectArea = true;
				}
				else if (rowIndex > s_StatisEditText.CursorDisplaySelectStartIndex[1] && rowIndex < s_StatisEditText.CursorDisplayIndex[1])
				{
					needDrawSelectArea = true;
				}
				else
				{
				}
			}
			else
			{
				if (rowIndex == s_StatisEditText.CursorDisplayIndex[1])
				{
					if (columIndex > s_StatisEditText.CursorDisplayIndex[0])
						needDrawSelectArea = true;
				}
				else if (rowIndex == s_StatisEditText.CursorDisplaySelectStartIndex[1])
				{
					if (columIndex <= s_StatisEditText.CursorDisplaySelectStartIndex[0])
						needDrawSelectArea = true;
				}
				else if (rowIndex > s_StatisEditText.CursorDisplayIndex[1] && rowIndex < s_StatisEditText.CursorDisplaySelectStartIndex[1])
				{
					needDrawSelectArea = true;
				}
				else
				{
				}
			}
		}

		return needDrawSelectArea;
	}

}
